import java.io.*;
import java.net.Socket;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;


public class Server implements Runnable{
    private static final Map<String, Socket> msgSocketMap = new ConcurrentHashMap<>();
    private static final Map<String, Socket> fileSocketMap = new ConcurrentHashMap<>();
    private static final Map<String, Socket> fileSendSockMap = new ConcurrentHashMap<>();
    private Socket socket;

    private String ID = null;

    private Scanner scanner;

    private PrintStream printStream;

    public Server(Socket socket){
        this.socket=socket;
    }


    @Override
    public void run() {
            try {
                //获取输入流
                this.scanner = new Scanner(socket.getInputStream());
                //获取输出流
                this.printStream = new PrintStream(socket.getOutputStream());
                String msg = null;

                /*确认socket类型
                  1.客户端与服务器通讯socket（“1”）
                  2.客户端接收消息socket（“2”）
                  3.客户端接收文件socket（“3”），一次性流，会多次连接，实时连接
                  4.客户端发送文件socket("4")，一次性流，会多次连接，发送文件时才连接
                  只有登录后才会创建2与3
                 */
                if(scanner.hasNextLine()){
                    msg = scanner.nextLine();
                    if(msg.equals("2")){
                        //将socket记录进socketListMap
                        //接收ID
                        this.ID = scanner.nextLine();
                        msgSocketMap.put(this.ID, socket);
                        return;
                    } else if (msg.equals("3")) {
                        //将socket记录进socketListMap
                        //接收ID
                        this.ID = scanner.nextLine();
                        fileSocketMap.put(this.ID, socket);
                        return;
                    }else if (msg.equals("4")){
                        //将socket记录进socketListMap
                        //接收ID
                        this.ID = scanner.nextLine();
                        fileSendSockMap.put(this.ID, socket);
                        return;
                    }
                }


                // 心跳包线程
                AliveComfirm aliveComfirm = new AliveComfirm(socket);
                Thread thread = new Thread(aliveComfirm);
                //抓线程异常
                thread.setUncaughtExceptionHandler(new UnchecckedExceptionhandler(socket));
                //线程开始
                thread.start();

                while (scanner.hasNextLine()) {
                    //处理客户端输入的字符串
                    msg = scanner.nextLine();
                    if(msg.equals("Login")){
                        login_check();
                    }else if(msg.equals("Register")){
                        Register register = new Register(socket, scanner);
                        register.registerMethod();
                    }else if(msg.equals("FriendList")){
                        sendFriendList();
                    }else if(msg.equals("AddFriend")){
                        addFriend();
                    }else if (msg.equals("SendMessage")){
                        sendMessage();
                    }else  if(msg.equals("SendFile")){
                        sendFile();
                    }
                }

            } catch (IOException | RuntimeException e) {
                e.printStackTrace();
            }

        //用户退出,更改相关数据
        if(this.ID != null){
            //Map
            msgSocketMap.remove(this.ID);
            fileSocketMap.remove(this.ID);
            //数据库
            Connection conn = DruidUtils.getConnection();
            String sql;
            PreparedStatement ps = null;
            ResultSet rs = null;
            //更改userState
            sql = "UPDATE user SET state = 0 WHERE userID = ?";
            try {
                ps = conn.prepareStatement(sql);
                ps.setInt(1, Integer.parseInt(ID));
                ps.executeUpdate();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }

            //关闭socket

            DruidUtils.closeAll(conn, ps, rs);
        }
    }

    public void login_check() {
        //连接数据库
        Connection conn = DruidUtils.getConnection();
        String msg = null;
        String pwd = null;
        String name = null;
        String check_ID = null;
        PreparedStatement ps = null;

        //获取输入流

        //userID
        check_ID = scanner.nextLine();
        //用ID查找pwd
        String sql;
        sql = "SELECT * FROM user WHERE userID = ?";
        ResultSet rs = null;

        try {
            assert conn != null;
            ps = conn.prepareStatement(sql);
            ps.setInt(1, Integer.parseInt(check_ID));
            rs = ps.executeQuery();
            //数据库内pwd
            rs.next();
            pwd = rs.getString("pwd");
            name = rs.getString("userName");
        } catch (SQLException e) {
            //ID未找到
            printStream.println("false");
            DruidUtils.closeAll(conn, ps, rs);
            return;
        } catch (NumberFormatException e) {
            //ID输入错误
            printStream.println("false");
            DruidUtils.closeAll(conn, ps, rs);
            return;
        }

        //用户输入pwd
        msg = scanner.nextLine();
        if (msg.equals(pwd)) {
            //登录成功
            this.ID = check_ID;
            //更改userState
            sql = "UPDATE user SET state = 1 WHERE userID = ?";
            try {
                ps = conn.prepareStatement(sql);
                ps.setInt(1, Integer.parseInt(ID));
                ps.executeUpdate();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }

            DruidUtils.closeAll(conn, ps, rs);
            printStream.println(name);
            return;
        }

        //密码错误返回false
        printStream.println("false");
        DruidUtils.closeAll(conn, ps, rs);
    }

    public void sendFriendList(){
        //连接数据库
        Connection conn = DruidUtils.getConnection();
        PreparedStatement ps = null;
        String sql;
        ResultSet rs = null;
        ResultSet rsFriendData = null;
        String friendID = null;

        sql = "SELECT * FROM friendship WHERE userID = ?";

        try {
            assert conn != null;
            ps = conn.prepareStatement(sql);
            ps.setString(1, this.ID);
            rs = ps.executeQuery();
            //数据库内pwd
            while (rs.next()){
                friendID = rs.getString("friendID").trim();

                sql = "SELECT * FROM user WHERE userID = ?";
                ps = conn.prepareStatement(sql);
                ps.setInt(1, Integer.parseInt(friendID));
                rsFriendData = ps.executeQuery();

                rsFriendData.next();

                String friendName = null;
                String friendState = null;

                friendID =  String.valueOf(rsFriendData.getInt("userID"));
                friendName =  rsFriendData.getString("userName");
                friendState =  String.valueOf(rsFriendData.getInt("state"));
                printStream.println(friendID);
                printStream.println(friendName);
                printStream.println(friendState);
            }
        } catch (SQLException | NumberFormatException e) {
            printStream.println("false");
        }

        DruidUtils.closeAll(conn, ps, rs);
        //结束短语
        printStream.println("EndOfList");
    }

    private void addFriend() {
        //连接数据库
        Connection conn = DruidUtils.getConnection();
        PreparedStatement ps = null;
        String sql;
        ResultSet rs = null;
        String friendID = null;

        //用户输入friendID
        friendID = scanner.nextLine();
        //添加好友关系
        try {
            assert conn != null;
            //确认好友ID是否存在
            sql = "SELECT * FROM user WHERE userID = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, Integer.parseInt(friendID));
            rs = ps.executeQuery();

            if(!rs.next()){
                //不存在，结束
                printStream.println("false");
                DruidUtils.closeAll(conn, ps, rs);
                return;
            }

            //确认关系是否已经存在
            sql = "SELECT * FROM friendship WHERE userID = ? and friendID = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, ID);
            ps.setString(2, friendID);
            rs = ps.executeQuery();

            if(rs.next()){
                //存在，结束
                printStream.println("false");
                DruidUtils.closeAll(conn, ps, rs);
                return;
            }

            //存在，添加关系
            sql = "INSERT INTO friendship(userID, friendID) values(?,?)";
            ps = conn.prepareStatement(sql);
            ps.setString(1, ID);
            ps.setString(2, friendID);
            ps.executeUpdate();

            //相互添加
            ps = conn.prepareStatement(sql);
            ps.setString(1, friendID);
            ps.setString(2, ID);
            ps.executeUpdate();

            //添加成功
            printStream.println("true");
        } catch (SQLException e) {
            //添加失败
            printStream.println("false");
        }catch (NumberFormatException e) {
            //ID输入错误
            printStream.println("false");
        }
        DruidUtils.closeAll(conn, ps, rs);
    }

    private void sendMessage(){
        String receiverID = scanner.nextLine();
        String message = scanner.nextLine();

            //接收方输出流
            try {
                Socket receiverSocket = msgSocketMap.get(receiverID);
                PrintStream receiverPrintStream = new PrintStream(receiverSocket.getOutputStream());
                receiverPrintStream.println(this.ID);
                receiverPrintStream.println(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
    }

    /**
     * 转发文件之前会有一个与客户端建立文件传输流的过程
     */
    private void sendFile(){
        String receiverID = scanner.nextLine();
        String sendFileName = null;


        try {
            //发送方文件发送流
            Thread.sleep(200);//等待senderSocker更新
            Socket senderSocker = fileSendSockMap.get(this.ID);


            //接收文件
            File directory = new File("src\\File_transfer");
            if(!directory.exists()) {
                directory.mkdir();
            }

            DataInputStream dataInputStream = new DataInputStream(senderSocker.getInputStream());
            PrintStream readyCheck = new PrintStream(senderSocker.getOutputStream());

            readyCheck.println("Ready");
            String fileName = scanner.nextLine();
            long fileLength = dataInputStream.readLong();

            sendFileName = fileName;
            //System.out.println(fileLength);

            File file = new File("src\\File_transfer" + "\\" + fileName);
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            /*while (true){
                long longlong = dataInputStream.readLong();
                System.out.println(longlong);
                if(longlong == -1){
                    break;
                }
            }*/

            // 开始接收文件
            byte[] bytes = new byte[1024];
            int length = 0;
            /*
            while (true){
                length = dataInputStream.read(bytes, 0, bytes.length);
                //System.out.print(length + "| ");
                fileOutputStream.write(bytes, 0, length);
                fileOutputStream.flush();
                //System.out.println(fileOutputStream.getChannel().size());

                if(length !=1024){
                    System.out.println("Loss");
                }


                if(fileOutputStream.getChannel().size() == fileLength){
                    break;
                }
            }*/

            readyCheck.println("Ready");

            while((length = dataInputStream.read(bytes, 0, bytes.length)) != -1) {
                fileOutputStream.write(bytes, 0, length);
                fileOutputStream.flush();

                if(fileOutputStream.getChannel().size() == fileLength){
                    break;
                }
                //System.out.println(fileOutputStream.getChannel().size());
            }

            //接收完成
            //关闭流

            fileOutputStream.close();
            dataInputStream.close();
            senderSocker.close();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        try{
            Socket receiverSocket = fileSocketMap.get(receiverID);
            PrintStream receiverPrintStream = new PrintStream(receiverSocket.getOutputStream());
            Scanner  receiverScaner= new Scanner(receiverSocket.getInputStream());
            //作为标志发给接收方，表明开始发送文件
            receiverPrintStream.println(this.ID);

            //发送文件
            FileInputStream fileInputStream;
            DataOutputStream dataOutputStream;
            File file = new File("src\\File_transfer" + "\\" + sendFileName);

            fileInputStream = new FileInputStream(file);
            dataOutputStream = new DataOutputStream(receiverSocket.getOutputStream());

            if(receiverScaner.hasNextLine()){
                String check = receiverScaner.nextLine();
                //System.out.println("Server check: " + check);
            }

            // 文件名和长度
            receiverPrintStream.println(file.getName());
            receiverPrintStream.flush();
            dataOutputStream.writeLong(file.length());
            dataOutputStream.flush();

            // 开始传输文件
            byte[] bytes = new byte[1024];
            int length = 0;

            if(receiverScaner.hasNextLine()){
                String check = receiverScaner.nextLine();
                //System.out.println("Server check: " + check);
            }

            while((length = fileInputStream.read(bytes, 0, bytes.length)) != -1) {
                dataOutputStream.write(bytes, 0, length);
                dataOutputStream.flush();
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}
